//
// Created by benxb on 2022/3/6.
//

#include "gtest/gtest.h"
#include <iostream>
#include <pqxx/pqxx>
#include <or_reader.h>
#include <ns_reader.h>
#include <kitti_reader.h>
#include <common/timer.h>
#include <common/ini_reader.h>

void CreateBlockTable() {
    pqxx::connection c{"postgresql://postgres:postgres@localhost/postgres"};
    pqxx::work txn{c};
    txn.exec("INSERT INTO pointcloud_formats (pcid, srid, schema) VALUES (1, 4326, '<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
             "        <pc:PointCloudSchema xmlns:pc=\"http://pointcloud.org/schemas/PC/1.1\"\n"
             "                            xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n"
             "            <pc:dimension>\n"
             "                <pc:position>1</pc:position>\n"
             "                <pc:size>4</pc:size>\n"
             "                <pc:description>X coordinate as a long integer. You must use the\n"
             "                    scale and offset information of the header to\n"
             "                    determine the double value.</pc:description>\n"
             "                <pc:name>X</pc:name>\n"
             "                <pc:interpretation>int32_t</pc:interpretation>\n"
             "                <pc:scale>0.001</pc:scale>\n"
             "            </pc:dimension>\n"
             "            <pc:dimension>\n"
             "                <pc:position>2</pc:position>\n"
             "                <pc:size>4</pc:size>\n"
             "                <pc:description>Y coordinate as a long integer. You must use the\n"
             "                    scale and offset information of the header to\n"
             "                    determine the double value.</pc:description>\n"
             "                <pc:name>Y</pc:name>\n"
             "                <pc:interpretation>int32_t</pc:interpretation>\n"
             "                <pc:scale>0.001</pc:scale>\n"
             "            </pc:dimension>\n"
             "            <pc:dimension>\n"
             "                <pc:position>3</pc:position>\n"
             "                <pc:size>4</pc:size>\n"
             "                <pc:description>Z coordinate as a long integer. You must use the\n"
             "                    scale and offset information of the header to\n"
             "                    determine the double value.</pc:description>\n"
             "                <pc:name>Z</pc:name>\n"
             "                <pc:interpretation>int32_t</pc:interpretation>\n"
             "                <pc:scale>0.001</pc:scale>\n"
             "            </pc:dimension>\n"
             "            <pc:metadata>\n"
             "                <Metadata name=\"compression\">None</Metadata>\n"
             "            </pc:metadata>\n"
             "        </pc:PointCloudSchema>');");
    txn.exec("CREATE TABLE patches( "
             "sweep SERIAL PRIMARY KEY,"
             "pa PCPATCH(1),"
             "tstamp TIMESTAMP NOT NULL)");
    txn.commit();
}

void DeleteBlockTable() {
    pqxx::connection c{"postgresql://postgres:postgres@localhost/postgres"};
    pqxx::work txn{c};

    txn.exec("DROP TABLE IF EXISTS patches");
    txn.exec("DELETE FROM pointcloud_formats");
    txn.exec("DROP INDEX IF EXISTS patches_tstamp_index");
    txn.commit();
}

void CreateBlockIndex() {
    pqxx::connection c{"postgresql://postgres:postgres@localhost/postgres"};
    pqxx::work txn{c};
    txn.exec("CREATE INDEX patches_tstamp_index ON patches(tstamp)");
    txn.commit();
}

void WritePoints(DataReader* data_reader) {
    benxdb::Timer timer;
    timer.Start();
    CreateBlockTable();
    timer.StopAndPrint("Init time.");

    // open data file
    std::vector<float> x_arr;
    std::vector<float> y_arr;
    std::vector<float> z_arr;

    pqxx::connection c{"postgresql://postgres:postgres@localhost/postgres"};
//    c.prepare("insert_point_patches", "INSERT INTO patches(pa, tstamp) values (PC_MakePatch(1, ARRAY[$1]), '2011-09-26 13:21:35.117716')");
    int scan_index = 0;
    while(data_reader->HasMoreSweep()) {
        scan_index++;
        data_reader->ReadSweep(x_arr, y_arr, z_arr);

        timer.Start();
        pqxx::work txn{c};
        std::string patches_str;
        for (int i = 0; i < x_arr.size(); i++) {
            if (i > 0) {
                patches_str.push_back(',');
            }
            patches_str += std::to_string(x_arr[i]) + ",";
            patches_str += std::to_string(y_arr[i]) + ",";
            patches_str += std::to_string(z_arr[i]);
        }
//        txn.exec_prepared("insert_point_patches", patches_str);
        txn.exec("INSERT INTO patches(pa, tstamp) values (PC_MakePatch(1, ARRAY["+patches_str+"]), '2011-09-26 13:21:35.117716')");
        timer.Pause();

        x_arr.clear();
        y_arr.clear();
        z_arr.clear();
    }

    timer.StopAndPrint("Load time.");
}

TEST(PgBlock, LoadNSData) {
    INIReader reader("config.ini");
    std::string data_dir = reader.Get("data", "data_dir", "");
    std::string ns01 = reader.Get("nuScenes", "01", "");
    DataReader* data_reader = new NSReader(data_dir + ns01);
    DeleteBlockTable();
    WritePoints(data_reader);
    delete data_reader;
}

TEST(PgBlock, LoadOR01Data) {
    INIReader reader("config.ini");
    std::string data_dir = reader.Get("data", "data_dir", "");
    std::string or01 = reader.Get("oxford", "01", "");
    DataReader* data_reader = new ORReader(data_dir + or01);
    DeleteBlockTable();
    WritePoints(data_reader);
    delete data_reader;
}

TEST(PgBlock, LoadOR02Data) {
    INIReader reader("config.ini");
    std::string data_dir = reader.Get("data", "data_dir", "");
    std::string or02 = reader.Get("oxford", "02", "");
    DataReader* data_reader = new ORReader(data_dir + or02);
    DeleteBlockTable();
    WritePoints(data_reader);
    delete data_reader;
}

TEST(PgBlock, LoadKT01Data) {
    INIReader reader("config.ini");
    std::string data_dir = reader.Get("data", "data_dir", "");
    std::string kt01 = reader.Get("kitti", "01", "");
    DataReader* data_reader = new ORReader(data_dir + kt01);
    DeleteBlockTable();
    WritePoints(data_reader);
    delete data_reader;
}

TEST(PgBlock, LoadKT02Data) {
    INIReader reader("config.ini");
    std::string data_dir = reader.Get("data", "data_dir", "");
    std::string kt02 = reader.Get("kitti", "02", "");
    DataReader* data_reader = new ORReader(data_dir + kt02);
    DeleteBlockTable();
    WritePoints(data_reader);
    delete data_reader;
}

int main(int argc, char **argv) {
    testing::InitGoogleTest(&argc,argv);
    return RUN_ALL_TESTS();
}